﻿using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;

namespace LiveFrameworkExplorer
{
    #region CryptoApi_SHA1_AES256

    internal sealed class CryptoApi_SHA1_AES256 : IDisposable
    {
        // Fields
        private const uint ALG_CLASS_DATA_ENCRYPT = 0x6000;
        private const uint ALG_CLASS_HASH = 0x8000;
        private const uint ALG_SID_AES_256 = 0x10;
        private const uint ALG_SID_SHA1 = 4;
        private const uint ALG_TYPE_ANY = 0;
        private const uint ALG_TYPE_BLOCK = 0x600;
        private const uint CALG_AES_256 = 0x6610;
        private const uint CALG_SHA1 = 0x8004;
        private const string CSP = "Microsoft Enhanced RSA and AES Cryptographic Provider";
        private const string CSP_XP = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)";
        private IntPtr hHash = IntPtr.Zero;
        private IntPtr hKey = IntPtr.Zero;
        private IntPtr hProv = IntPtr.Zero;
        private const int NTE_KEYSET_NOT_DEF = -2146893799;

        // Methods
        internal CryptoApi_SHA1_AES256()
        {
            if (!CAPIInterop.CryptAcquireContext(ref this.hProv, null, "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)", 0x18, 0xf0000000))
            {
                if (-2146893799 != Marshal.GetHRForLastWin32Error())
                {
                    throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
                }
                if (!CAPIInterop.CryptAcquireContext(ref this.hProv, null, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18, 0xf0000000))
                {
                    throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
                }
            }
        }

        private void CreateHash(uint algId)
        {
            if (!CAPIInterop.CryptCreateHash(this.hProv, algId, IntPtr.Zero, 0, ref this.hHash))
            {
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
            }
        }

        private void CryptDecrypt(byte[] pData, ref uint dataLen)
        {
            if (!CAPIInterop.CryptDecrypt(this.hKey, IntPtr.Zero, 1, 0, pData, ref dataLen))
            {
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
            }
        }

        private void CryptEncrypt(byte[] pData, ref uint dataLen, uint bufLen)
        {
            if (!CAPIInterop.CryptEncrypt(this.hKey, IntPtr.Zero, 1, 0, pData, ref dataLen, bufLen))
            {
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
            }
        }

        internal byte[] DecryptData(byte[] pData)
        {
            uint length = (uint)pData.Length;
            this.CryptDecrypt(pData, ref length);
            byte[] buffer = new byte[length];
            for (int i = 0; i < length; i++)
            {
                buffer[i] = pData[i];
            }
            return buffer;
        }

        internal void DeriveKey(string password, string salt)
        {
            if (this.hHash == IntPtr.Zero)
            {
                this.CreateHash(0x8004);
            }
            this.HashData(Encoding.Unicode.GetBytes(password));
            this.HashData(Encoding.Unicode.GetBytes(salt));
            if (!CAPIInterop.CryptDeriveKey(this.hProv, 0x6610, this.hHash, 0, ref this.hKey))
            {
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
            }
        }

        internal byte[] EncryptData(string data)
        {
            byte[] bytes = Encoding.Unicode.GetBytes(data);
            uint length = (uint)bytes.Length;
            uint bufLen = length;
            this.CryptEncrypt(null, ref length, bufLen);
            byte[] array = new byte[length];
            bytes.CopyTo(array, 0);
            this.CryptEncrypt(array, ref bufLen, (uint)array.Length);
            return array;
        }

        private void HashData(byte[] pData)
        {
            if (!CAPIInterop.CryptHashData(this.hHash, pData, (uint)pData.Length, 0))
            {
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), IntPtr.Zero);
            }
        }

        void IDisposable.Dispose()
        {
            if (this.hKey != IntPtr.Zero)
            {
                CAPIInterop.CryptDestroyKey(this.hKey);
            }
            if (this.hHash != IntPtr.Zero)
            {
                CAPIInterop.CryptDestroyHash(this.hHash);
            }
            if (this.hProv != IntPtr.Zero)
            {
                CAPIInterop.CryptReleaseContext(this.hProv, 0);
            }
        }

        // Nested Types
        internal static class CAPIInterop
        {
            // Methods
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptAcquireContext(ref IntPtr hProv, string pszContainer, string csp, int cspProv, uint flags);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptCreateHash(IntPtr hProv, uint algId, IntPtr hKey, uint dwProvType, ref IntPtr phHash);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, byte[] pbData, ref uint pdwDataLen);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptDeriveKey(IntPtr hProv, uint Algid, IntPtr hBaseData, int flags, ref IntPtr phKey);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptDestroyHash(IntPtr hHash);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptDestroyKey(IntPtr hKey);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptEncrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, byte[] pbData, ref uint pdwDataLen, uint dwBufLen);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptHashData(IntPtr hHash, byte[] pbData, uint dataLen, uint flags);
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", SetLastError = true)]
            internal static extern bool CryptReleaseContext(IntPtr hProv, int mbz);
        }

        [Flags]
        private enum CRYPT_FLAGS : uint
        {
            CRYPT_DELETEKEYSET = 0x10,
            CRYPT_MACHINE_KEYSET = 0x20,
            CRYPT_NEWKEYSET = 8,
            CRYPT_SILENT = 0x40,
            CRYPT_VERIFYCONTEXT = 0xf0000000
        }

        private enum CSP_PROV
        {
            PROV_RSA_AES = 0x18
        }
    }

    #endregion

    #region LiveMeshLocal

    internal static class LiveMeshLocal
    {
        private static string CachedLocalAuthHeader;
        private static string CachedSessionKey;

        internal static Uri GetLocalServiceUri()
        {
            int num = 0x800;
            try
            {
                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Live Mesh\Moe"))
                {
                    num = (int)key.GetValue("Port");
                }
            }
            catch (Exception) { }
            return new Uri(string.Format("http://localhost:{0}/", num));
        }
 
        internal static string GetLocalAuthToken(string baseUrl)
        {
            try
            {
                string str = null;
                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Live Mesh\Moe"))
                {
                    str = (string)key.GetValue("SessionKey");
                }
                if (string.IsNullOrEmpty(str))
                {
                    return null;
                }
                if (str == CachedSessionKey)
                {
                    return CachedLocalAuthHeader;
                }
                using (CryptoApi_SHA1_AES256 i_sha_aes = new CryptoApi_SHA1_AES256())
                {
                    i_sha_aes.DeriveKey(str, "LiveFX-Local-Authenticator");
                    CachedLocalAuthHeader = Convert.ToBase64String(i_sha_aes.EncryptData(baseUrl));
                    CachedSessionKey = str;
                }
            }
            catch (Exception) { }
            return CachedLocalAuthHeader;
        }
    }

    #endregion
}
